package com.kaesun.ufit.tools;

import java.io.File;
import java.io.FileOutputStream;

import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class CalibrationView extends View {
	private static final String TAG = "CalibrationView";
	private void log(String log) {
		Log.i(TAG, log);
	}

	private static final float ADC_RESOLUTION = (float)4096.0;
	private static final float DEF_SCALE = 65536;
	private static final int DEF_OFFSET = 100;//50;
	private static final int DEF_POINT = 5;

	private static final String DEF_CALIBRATION = "0 1 0 0 0 1 1";	// ox, ax, bx, oy, ay, by, scale 
	private static final String DEV_PATHNAME = "/sys/devices/platform/omap/ti_tscadc/tsc/calibrate";
	private static final String DEV_PATHNAME2 = "/calibrate";

	private float mKX = 1;
	private float mKY = 1;
	private PointF[] mCalibrationPoints = new PointF[DEF_POINT];
	private PointF[] mTouchAdcs = new PointF[DEF_POINT];
	private PointF[] mDisplayAdcs = new PointF[DEF_POINT];
	private PointF mLastPoint = new PointF();
	private int mCurPointStep = -1;

	private final Paint mPointPaint = new Paint();

	private Context mContext = null;

	public CalibrationView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		init();
	}

	public CalibrationView(Context context, AttributeSet attrs) {
		super(context, attrs);

		init();
	}

	public CalibrationView(Context context) {
		super(context);

		init();
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		mKX = w / ADC_RESOLUTION;
		mKY = h / ADC_RESOLUTION;
		log("kx=" + mKX + ", ky=" + mKY);

		mCalibrationPoints[0].set(DEF_OFFSET, DEF_OFFSET);
		mCalibrationPoints[1].set(w-DEF_OFFSET, DEF_OFFSET);
		mCalibrationPoints[2].set(w-DEF_OFFSET, h-DEF_OFFSET);
		mCalibrationPoints[3].set(DEF_OFFSET, h-DEF_OFFSET);
		mCalibrationPoints[4].set(w/2, h/2);

		for (int i=0; i<DEF_POINT; i++) {
			mTouchAdcs[i].set(0, 0);
			mDisplayAdcs[i].set((mCalibrationPoints[i].x / mKX), (mCalibrationPoints[i].y / mKY));
		}

		mLastPoint.set(-1, -1);
		mCurPointStep = -1;

		super.onSizeChanged(w, h, oldw, oldh);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		if (0 > mCurPointStep) {
			if ((0 <= mLastPoint.x) && (0 <= mLastPoint.y)) {
				drawPoint(canvas, mLastPoint);
			}
		} else {
			for (int i=0; i<mCalibrationPoints.length; i++) {
				if (mCurPointStep == i) {
					drawCross(canvas, mCalibrationPoints[i]);
				}
			}
		}
	}


	public void setContext(Context context) {
		mContext = context;
	}

	public boolean touchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
		case MotionEvent.ACTION_MOVE:
			mLastPoint.set(event.getX(), event.getY());
			break;
		case MotionEvent.ACTION_UP:
			if (0 > mCurPointStep) {
				mLastPoint.set(-1, -1);
			} else {
				mLastPoint.set(event.getX(), event.getY());

				mTouchAdcs[mCurPointStep].set((mLastPoint.x / mKX), (mLastPoint.y / mKY));
				log("step=" + mCurPointStep + ", x=" + mTouchAdcs[mCurPointStep].x + ", y=" + mTouchAdcs[mCurPointStep].y);

				mCurPointStep++;
				if (5 == mCurPointStep) {
					mCurPointStep = -1;
					updateCalibration(0);
				}
			}
			break;

		default:
			return true;
		}

		invalidate();

		return true;
	}

	public void calbration() {
		writeFile(DEV_PATHNAME, DEF_CALIBRATION);

		mLastPoint.set(-1, -1);
		mCurPointStep = 0;

		invalidate();
	}

	public boolean isCalibration() {
		return (0 > mCurPointStep) ? false : true;
	}


	private void init() {
		for (int i=0; i<DEF_POINT; i++) {
			mCalibrationPoints[i] = new PointF(0, 0);
			mTouchAdcs[i] = new PointF(0, 0);
			mDisplayAdcs[i] = new PointF(0, 0);
		}

		mPointPaint.setColor(Color.RED);
		mPointPaint.setAntiAlias(true);
	}

	private void drawPoint(Canvas canvas, PointF point) {
		canvas.drawLine(point.x-20, point.y, point.x+20, point.y, mPointPaint);
		canvas.drawLine(point.x, point.y-20, point.x, point.y+20, mPointPaint);
	}

	private void drawCross(Canvas canvas, PointF point) {
		drawCross(canvas, point.x, point.y);
	}
	private void drawCross(Canvas canvas, float x, float y) {
		canvas.drawLine(x - 20, y, 1 + (x + 20), y, mPointPaint);
		canvas.drawLine(x, y - 20, x, 1 + (y + 20), mPointPaint);
		canvas.drawLine(x - 20, y - 20, 1 + (6 + (x - 20)), y - 20, mPointPaint);
		canvas.drawLine(x - 20, y - 20, x - 20, 1 + (6 + (y - 20)), mPointPaint);
		canvas.translate(0.0F, 0.0F);
		canvas.drawLine(x - 20, 1 + (y + 20), x - 20, y + 20 - 6, mPointPaint);
		canvas.translate(0.0F, 0.0F);
		canvas.drawLine(x - 20, y + 20, 1 + (6 + (x - 20)), y + 20, mPointPaint);
		canvas.drawLine(1 + (x + 20), y + 20, x + 20 - 6, y + 20, mPointPaint);
		canvas.drawLine(x + 20, 1 + (y + 20), x + 20, y + 20 - 6, mPointPaint);
		canvas.drawLine(1 + (x + 20), y - 20, x + 20 - 6, y - 20, mPointPaint);
		canvas.drawLine(x + 20, y - 20, x + 20, 1 + (6 + (y - 20)), mPointPaint);
	}

	private void writeFile(String pathName, String data) {
		File localFile = new File(pathName);
		try {
			new FileOutputStream(localFile).write(data.getBytes());
		} catch (Exception e) {
			AlertDialog.Builder adBuilder = new AlertDialog.Builder(mContext);
			adBuilder.setMessage(e.getMessage());
			adBuilder.setNegativeButton("OK", null);
			adBuilder.show();
		}
	}

	private void updateCalibration(int type) {
		float calDatas[] = {0, 1, 0, 0, 0, 1, 1};	// ox, ax, bx, oy, ay, by, scale

		if (0 == type) {
			float a=0, b=0, c=0, d=0, e=0;
			float x1=0, x2=0, x3=0, y1=0, y2=0, y3=0;
			float offset_total, offset_temp;

			for (int i=0; i<5; i++) {
				a  += (mTouchAdcs[i].x * mTouchAdcs[i].x);
				b  += (mTouchAdcs[i].y * mTouchAdcs[i].y);
				c  += (mTouchAdcs[i].x * mTouchAdcs[i].y);
				d  += (mTouchAdcs[i].x);
				e  += (mTouchAdcs[i].y);
				x1 += (mTouchAdcs[i].x * mDisplayAdcs[i].x);
				x2 += (mTouchAdcs[i].y * mDisplayAdcs[i].x);
				x3 += (mDisplayAdcs[i].x);
				y1 += (mTouchAdcs[i].x * mDisplayAdcs[i].y);
				y2 += (mTouchAdcs[i].y * mDisplayAdcs[i].y);
				y3 += (mDisplayAdcs[i].y);
			}
			offset_total = 5*(a*b-(c*c)) + 2*c*d*e - a*e*e - b*d*d;

			offset_temp = 5*(x1*b-x2*c) + e*(x2*d - x1*e) + x3*(c*e - b*d);
			//_alpha_x = offset_temp / offset_total;
			calDatas[1] = offset_temp / offset_total;

			offset_temp = 5*(x2*a-x1*c) + d*(x1*e - x2*d) + x3*(c*d - a*e);
			//_beta_x = offset_temp / offset_total;
			calDatas[2] = offset_temp / offset_total;

			offset_temp = x3*(a*b-c*c) + x1*(c*e - b*d) + x2*(c*d - a*e);
			//_offset_x = offset_temp / offset_total;
			calDatas[0] = offset_temp / offset_total;

			offset_temp = 5*(y1*b-y2*c) + e*(y2*d - y1*e) + y3*(c*e - b*d);
			//_alpha_y = offset_temp / offset_total;
			calDatas[4] = offset_temp / offset_total;

			offset_temp = 5*(y2*a-y1*c) + d*(y1*e - y2*d) + y3*(c*d - a*e);
			//_beta_y = offset_temp / offset_total;
			calDatas[5] = offset_temp / offset_total;

			offset_temp = y3*(a*b-c*c) + y1*(c*e - b*d) + y2*(c*d - a*e);
			//_offset_y = offset_temp / offset_total;
			calDatas[3] = offset_temp / offset_total;

			//newX = (u16)(((*pX) * _alpha_x) + ((*pY) * _beta_x) + _offset_x + 0.5);
			//newY = (u16)(((*pX) * _alpha_y) + ((*pY) * _beta_y) + _offset_y + 0.5);
		} else {
			float a[] = {0,0,0}, b[] = {0,0,0}, c[] = {0,0,0}, d[] = {0,0,0}, k;
			for (int i=0; i<5; i++) {
				a[2] = a[2] + mTouchAdcs[i].x;
				b[2] = b[2] + mTouchAdcs[i].y;
				c[2] = c[2] + mDisplayAdcs[i].x;
				d[2] = d[2] + mDisplayAdcs[i].y;
				a[0] = a[0] + (mTouchAdcs[i].x * mTouchAdcs[i].x);
				a[1] = a[1] + (mTouchAdcs[i].x * mTouchAdcs[i].y);
				b[0] = a[1];
				b[1] = b[1] + (mTouchAdcs[i].y * mTouchAdcs[i].y);
				c[0] = c[0] + (mTouchAdcs[i].x * mDisplayAdcs[i].x);
				c[1] = c[1] + (mTouchAdcs[i].y * mDisplayAdcs[i].x);
				d[0] = d[0] + (mTouchAdcs[i].x * mDisplayAdcs[i].y);
				d[1] = d[1] + (mTouchAdcs[i].y * mDisplayAdcs[i].y);
			}

			a[0] = a[0] / a[2];
			a[1] = a[1] / b[2];
			b[0] = b[0] / a[2];
			b[1] = b[1] / b[2];
			c[0] = c[0] / a[2];
			c[1] = c[1] / b[2];
			d[0] = d[0] / a[2];
			d[1] = d[1] / b[2];
			a[2] = a[2] / 5;
			b[2] = b[2] / 5;
			c[2] = c[2] / 5;
			d[2] = d[2] / 5;

			k = (a[0]-a[2])*(b[1]-b[2]) - (a[1]-a[2])*(b[0]-b[2]);

			//_KX1 = ((c[0]-c[2])*(b[1]-b[2]) - (c[1]-c[2])*(b[0]-b[2])) / k;
			calDatas[1] = ((c[0]-c[2])*(b[1]-b[2]) - (c[1]-c[2])*(b[0]-b[2])) / k;

			//_KX2 = ((c[1]-c[2])*(a[0]-a[2]) - (c[0]-c[2])*(a[1]-a[2])) / k;
			calDatas[2] = ((c[1]-c[2])*(a[0]-a[2]) - (c[0]-c[2])*(a[1]-a[2])) / k;

			//_KX3 = (b[0]*(a[2]*c[1] - a[1]*c[2]) + b[1]*(a[0]*c[2] - a[2]*c[0]) + b[2]*(a[1]*c[0] - a[0]*c[1])) / k;
			calDatas[0] = (b[0]*(a[2]*c[1] - a[1]*c[2]) + b[1]*(a[0]*c[2] - a[2]*c[0]) + b[2]*(a[1]*c[0] - a[0]*c[1])) / k;

			//_KY1 = ((d[0]-d[2])*(b[1]-b[2]) - (d[1]-d[2])*(b[0]-b[2])) / k;
			calDatas[4] = ((d[0]-d[2])*(b[1]-b[2]) - (d[1]-d[2])*(b[0]-b[2])) / k;

			//_KY2 = ((d[1]-d[2])*(a[0]-a[2]) - (d[0]-d[2])*(a[1]-a[2])) / k;
			calDatas[5] = ((d[1]-d[2])*(a[0]-a[2]) - (d[0]-d[2])*(a[1]-a[2])) / k;

			//_KY3 = (b[0]*(a[2]*d[1] - a[1]*d[2]) + b[1]*(a[0]*d[2] - a[2]*d[0]) + b[2]*(a[1]*d[0] - a[0]*d[1])) / k;
			calDatas[3] = (b[0]*(a[2]*d[1] - a[1]*d[2]) + b[1]*(a[0]*d[2] - a[2]*d[0]) + b[2]*(a[1]*d[0] - a[0]*d[1])) / k;

			//newX = (u16)((_KX1 * (*pX)) + (_KX2 * (*pY)) + _KX3 + 0.5);
			//newY = (u16)((_KY1 * (*pX)) + (_KY2 * (*pY)) + _KY3 + 0.5);
		}
		calDatas[6] = DEF_SCALE;

		String calData = "";
		for (int i=0; i<6; i++) {
			calData += (Math.round(calDatas[i] * DEF_SCALE) + " ");
		}
		calData += Math.round(calDatas[6]);
		log("calData=" + calData);

		writeFile(DEV_PATHNAME, calData);
		writeFile(DEV_PATHNAME2, calData);
	}
}
